<?php
class Application_Model_misc
{
	public function __construct()
	{

	}
	
	/*
  	 * 從指定的csv檔裡，撈出全部資料或指定ID的資料。
  	 * (有可能是使用者csv檔的內容格式輸入錯誤，造成撈不到任何資料而傳出空陣列。)
  	 * @param str $csvFName CSV檔的檔名。
  	 * @param $itemId NULL:表示撈全部資料；int:表示撈指定ID的資料；ary:表示依照
  	 * 陣列裡紀錄的資料索引值的順序來撈出資料。
  	 * 
  	 * @return ary $ary_items 資料
  	 */
	protected function getFromCSV($csvFName, $itemId=NULL)
	{
		//為了讓繁體中文能正常顯示。
		setlocale(LC_ALL, 'zh_TW.UTF-8');
		
		$row = 1;
		$ary_items = array();
		if (($handle = fopen($csvFName, "r")) !== FALSE) 
		{
			switch(TRUE)
			{
				//表示撈csv檔裡面全部的商品資料。
				case (is_null($itemId)):
					while (($data = fgetcsv($handle, 0, ",")) !== FALSE) 
				    {
				        array_push($ary_items, $data);
				    }
					break;

				//表示依照傳入的商品ID陣列順序，從csv檔撈出對應的商品資料。
				case (is_array($itemId));
					foreach($itemId as $eachItemId)
					{
						while (($data = fgetcsv($handle, 0, ",")) !== FALSE) 
					    {
					    	if($eachItemId == $data[0])
					        	array_push($ary_items, $data);
					    }
					    rewind($handle);
					}
					break;
				
				//表示只傳入一個商品ID，只要撈一筆符合的商品資料。
				default:
					while (($data = fgetcsv($handle, 0, ",")) !== FALSE) 
				    {
				        if($itemId == $data[0])
				        {
				        	array_push($ary_items, $data);
				        	break;
				        }
				    }
					break;
			}
		    fclose($handle);
		}
		return $ary_items;
	}
	
	/*
	 * 接收使用者上傳的CSV檔。
	 * @param str $newCSVName 上傳之後新的CSV檔名。
	 */
	protected function doCSVUpl($newCSVName)
	{
		$flTrans = new Zend_File_Transfer_Adapter_Http ();
		$flTrans->setDestination('.');
		
		////檢查副檔名是否為"csv"，如果不是，回傳錯誤訊息。
		$fileName = $flTrans->getFileName(NULL, FALSE);
		$fileExtension = strtolower(substr($fileName, strrpos($fileName, '.')));
		if('.csv' != $fileExtension)
		{
			$ary_rsp = array(
				'success' => FALSE,
				'errMsg' => '請上傳正確的CSV檔！'
			);
		}
		else 
		{
			$flTrans->addFilter('Rename', array('target' => $newCSVName, 'overwrite' => TRUE));
			if(! $flTrans->receive())
			{
				$ary_rsp = array(
					'success' => FALSE,
	            	'errMsg' => implode("\n", $flTrans->getMessages())
				);
			}
			else 
				$ary_rsp = array('success' => TRUE);
		}

		return $ary_rsp;
	}
	
    /**
	 * 先產生n字元小寫英文字母的亂碼。
	 * @param int $n 亂碼的長度
	 * 
	 * @return str n字元小寫英文字母的亂碼。
	 */
	private function _getRandomStr($n)
	{
		$figlet = new Zend_Captcha_Figlet(array('wordLen' => $n));
		$figlet->generate();
		return $figlet->getWord();
	}
	
	private function _delTree($dir)
    {  
    	$files = glob( $dir . '/*', GLOB_MARK );  
    	foreach( $files as $file )
    	{  
        	if( substr( $file, -1 ) == '/' )  
            	$this->_delTree( $file );  
        	else  
            	unlink( $file );  
	    }   
	}
	
	protected function _isZip()
	{
		$ary_info = current($_FILES);
		$storedPath = $ary_info['tmp_name'];

		////利用"pclzip"套件偵測是否為它可以接受的壓縮檔。
		require_once('pclzip.lib.php');
		$archive = new PclZip($storedPath);
		if(0 === $archive->properties() &&
			'PCLZIP_ERR_BAD_FORMAT' === $archive->errorName()
		)
		{
			return FALSE;
		}
		else
		{
			return TRUE;
		}
	}

	/**
     * 處理zip檔案上傳。
     * @param ary $input
     * @param str $storePath
     * @parem obj $flTrans
     */
    private function _zipUpload($storePath, $flTrans)
    {
    	//取得壓縮檔檔名。
        $fileName = $flTrans->getFileName(NULL, FALSE);
        
        //清空先前的圖片。
        $this->_delTree($storePath);
        
        $ary_rsp = array();
    	if($flTrans->receive())
    	{
			require_once('pclzip.lib.php');
			$archive = new PclZip($storePath . '/' . $fileName);
			
			//解壓縮。
			$list = $archive->extract(PCLZIP_OPT_PATH, $storePath);
			
			////為了避開瀏覽器的快取，重新命名成"原本檔名_10字元亂碼.原本副檔名"。
			////例：管理者兩次上傳的檔案的名稱相同，因為瀏覽器快取，即使兩次圖片內容不一樣，瀏覽器都只會顯示前一次的圖片。
    		foreach($list as $eachEl)
			{
				$ary = explode('.', $eachEl['filename']);
				$lastStrSegment = array_pop($ary);
				$newFileName = implode('.', $ary) . '_' . $this->_getRandomStr(10) . '.' . $lastStrSegment;
				rename($eachEl['filename'], $newFileName);
			}
			
			//刪除zip。
			unlink($storePath . '/' . $fileName);
			
			$ary_rsp['success'] = TRUE;

		}
		else
		{
			$ary_rsp['success'] = FALSE;
			$ary_rsp['errMsg'] = implode("\n", $flTrans->getMessages());    
		}
		
		return $ary_rsp;
    }
    
   /**
	* @desc Return the MIME type for an image file
	*
	* @param string $image_path Path to image file
	*/
	private function _get_image_type($image_path)
	{
	    if ( ! file_exists($image_path))
	    {
	        error_log('The file "'.$image_path.'" does not exist in function '.__FUNCTION__.' on line '.__LINE__.' of file '.__FILE__);
	        
	        return FALSE;
	    }

	    $itype = FALSE;
	
	    ////如果有安裝"exif"套件，就使用它來取得mime類型。
	    if(function_exists('exif_imagetype') && function_exists('image_type_to_mime_type'))
	    {
	    	if($mimeType = @exif_imagetype($image_path))
	    	{
	    		return $itype = image_type_to_mime_type($mimeType);
	    	}
	    	////如果這個檔案不是圖片，回傳FALSE;
	    	else 
	    	{
	    		return $itype = FALSE;
	    	}
	    }
	    ////如果有安裝"gd"套件，就使用它來取得mime類型。
	    elseif(function_exists('getimagesize'))
	    {
	    	if($info = @getimagesize($image_path))
	    	{
	        	return $itype = image_type_to_mime_type($info[2]);
	    	}
	    	////如果這個檔案不是圖片，回傳FALSE;
	    	else 
	    	{
	    		return $itype = FALSE;
	    	}
		}
		////最後只好直接擷取副檔名，不過這不準確...
	    else
	    {
			$type = pathinfo($image_path, PATHINFO_EXTENSION);
	    	switch(strtolower($type))
	        {
	            case 'gif':     $itype = 'image/gif';
	                            break;
	            case 'png':     $itype = 'image/png';
	                            break;
	            case 'jpg':     $itype = 'image/jpeg';
	                            break;
	            case 'jpeg':    $itype = 'image/jpeg';
	                            break;
	            case 'swf':     $itype = 'application/x-shockwave-flash';
	                            break;
	            case 'psd':     $itype = 'image/psd';
	                            break;
	            case 'bmp':     $itype = 'image/bmp';
	                            break;
	            case 'tiff':    $itype = 'image/tiff';
	                            break;
	            case 'jpc':     $itype = 'application/octet-stream';
	                            break;
	            case 'jp2':     $itype = 'image/jp2';
	                            break;
	            case 'jpx':     $itype = 'application/octet-stream';
	                            break;
	            case 'jb2':     $itype = 'application/octet-stream';
	                            break;
	            case 'swc':     $itype = 'application/x-shockwave-flash';
	                            break;
	            case 'iff':     $itype = 'image/iff';
	                            break;
	            case 'wbmp':    $itype = 'image/vnd.wap.wbmp';
	                            break;
	            case 'xbm':     $itype = 'image/xbm';
	                            break;
	            case 'ico':     $itype = 'image/vnd.microsoft.icon';
	                            break;
	            case '':
	            	$itype = FALSE;
	            	break;
	            	
	            default:        $itype = 'application/octet-stream';
	        }
	        return $itype;
		}
	}
	
	/**
	 * 檢查是否為圖片？
	 * @param str $storedPath 檔案儲存位置。
	 * 
	 * @return bool $isImage 是否為圖片？
	 */
	protected function _isImage()
	{
		$ary_info = current($_FILES);
		$storePath = $ary_info['tmp_name'];

		//想法是：如果伺服器沒有安裝"fileinfo"套件，那麼就無法使用zend的驗證器(IsImage)。
		//如果接連"exif"、"gd"都不能取得mime類型，最後只好直接擷取副檔名。
		$imageType = $this->_get_image_type($storePath);

		switch(TRUE)
		{
			case 'image/gif' === $imageType:
			case 'image/png' === $imageType:
			case 'image/jpeg' === $imageType:
			case 'image/bmp' === $imageType:
				return TRUE;
			
			case FALSE === $imageType:
			default:
				return FALSE;
		}
	}
    
	/**
     * 處理單張圖片上傳。
     * @param ary $input
     * @param str $storePath
     * @parem obj $flTrans
     */
    private function _imageUpload($flTrans)
    {
    	$ary_rsp = array();
		if($flTrans->receive())
		{
			//取得圖片檔名。
        	$fileName = $flTrans->getFileName(NULL, TRUE);
        	
        	////為了避開瀏覽器的快取，重新命名成"原本檔名_10字元亂碼.原本副檔名"。
			////例：管理者兩次上傳的檔案的名稱相同，因為瀏覽器快取，即使兩次圖片內容不一樣，瀏覽器都只會顯示前一次的圖片。
			$ary = explode('.', $fileName);
			$lastStrSegment = array_pop($ary);
			$newFileName = implode('.', $ary) . '_' . $this->_getRandomStr(10) . '.' . $lastStrSegment;
			rename($fileName, $newFileName);
			
			$ary_rsp['success'] = TRUE;
		}
		else
		{
			$ary_rsp['success'] = FALSE;
			$ary_rsp['message'] = implode("\n", $flTrans->getMessages());
		}
	
        return $ary_rsp;
    }
    
	/*
	 * 接收使用者上傳的圖片。
	 * 有兩種情況：
	 * 1-使用者上傳單張圖片，附加圖片到對應的資料夾裡。
	 * 2-使用者上傳zip壓縮檔，先刪光對應的資料夾裡所有的圖片，再解壓縮到此資料夾裡面。
	 * 
	 */
	protected function doImgUpl($storePath)
	{
		$flTrans = new Zend_File_Transfer_Adapter_Http();
		
		$flTrans->setDestination($storePath);

		////檢查副檔名是否為"zip"。
		$ary_rsp = array();
		if(extension_loaded('fileinfo'))
		{
			$flTrans->addValidator('IsCompressed', false, 'zip');
		}
        if($flTrans->isValid() && $this->_isZip())
        {
			$ary_rsp = $this->_zipUpload($storePath, $flTrans);
        }
        ////檢查使用者是否上傳的是圖片。
       else
       {
			if(extension_loaded('fileinfo'))
			{
	        	$flTrans->removeValidator('IsCompressed');
	        	$flTrans->addValidator('IsImage', false);
			}
        	if($flTrans->isValid() && $this->_isImage())
        	{
				$ary_rsp = $this->_imageUpload($flTrans);
        	}
        	else
        	{
        		$ary_rsp['success'] = FALSE;
        		$ary_rsp['errMsg'] = "請上傳圖片或zip檔案!";
        	}
        }
		
		return $ary_rsp;
	}
	
	/*
	 * 將字串上面的亂碼拿掉。
	 * @param str $str 字串
	 * @param str $prefix 亂碼起始識別字元
	 * @param str $suffix 亂碼結束識別字元
	 * @param bool $isAttachedSuffix 是否附加上亂碼結束識別字元(例如：處理檔名)？
	 * 
	 * @return str $newStr 拿掉亂碼後的字串
	 */
	protected function _doRndCodeFilter($str, $prefix, $suffix, $isAttachedSuffix=False)
	{
		$ary = explode($prefix, $str);
		$str_rightSegment = array_pop($ary);
		$ary_rightSegment = explode($suffix,  $str_rightSegment);
		$str_lastSegment = array_pop($ary_rightSegment);
		
		switch(TRUE)
		{
			case TRUE === $isAttachedSuffix:
				$newStr = implode($prefix, $ary) . $suffix . $str_lastSegment;
				return $newStr;
				
			case FALSE === $isAttachedSuffix:
				$newStr = implode($prefix, $ary) . $str_lastSegment;
				return $newStr;
		}
	}
	
	/**
	 * 取得指定目錄底下，對應的插入亂數的儲存檔名(為了避開瀏覽器快取特性，才插入亂數)。
	 * @param str $fileName 指定的檔名，可接受空字串
	 * @param str $directory 目錄的路徑(預設值從'public/'開始算起)，
	 * 如果要取得'public/image/item/'底下的檔案清單，就傳入參數'./image/item'。
	 * 
	 * @return str $storedFileName 真實的儲存檔名(如果傳入空字串或找不到檔案，就回傳空字串)
	 */
	protected function _getStoredFileName($fileName, $directory)
	{
		if(empty($fileName))
		{
			return $storedFileName = '';
		}
		else 
		{
			////從目錄裡撈出檔案列表，去掉亂碼後，比對是否存在對應的圖片。
			$ary_fileList = $this->getDirectoryList($directory);
			foreach($ary_fileList as $eachFileName)
			{
				//將檔名中的亂碼去掉，拿剩餘檔名來作為比對條件。
				$str = $this->_doRndCodeFilter($eachFileName, '_', '.', TRUE);
				
				if($str === $fileName)
				{
					return $storedFileName = $eachFileName;
				}
			}
			
			return $storedFileName = '';
		}
	}
	
	/*
	 * 取得指定目錄底下的檔案清單。
	 * @param str $directory 目錄的路徑(預設值從'public/'開始算起)，
	 * 如果要取得'public/image/item/'底下的檔案清單，就傳入參數'./image/item'。
	 * 
	 * @return ary $results 指定目錄底下的檔案清單(但不包含隱藏檔)。
	 */
	protected function getDirectoryList($directory='./') 
  	{
	    // create an array to hold directory list
	    $results = array();
	
	    // create a handler for the directory
	    $handler = opendir($directory);
	
	    // open directory and walk through the filenames
	    while ($file = readdir($handler)) 
	    {
	      // if file isn't this directory or its parent, add it to the results
	      if ($file != '.' && $file != '..' && 0 != strpos($file, '.')) 
	      {
	        $results[] = $file;
	      }
	
	    }
	
	    // tidy up: close the handler
	    closedir($handler);
	
	    // done!
	    return $results;
  	}
  	
  	/*
  	 * 從傳入的陣列中亂數挑出指定數量的商品資料。
  	 * @param ary $ary 裝有商品資料的陣列
  	 * @param int $amt 指定的數量(預設為全部；如果指定大於商品資料筆數，就回傳洗牌過後的全部商品資料陣列)
  	 * 
  	 * @return ary $ary_randItem 亂數取得的商品資料。
  	 */
  	protected function getRandElems($ary, $amt=0)
  	{
  		////如果使用者指定數量小於全部商品資料筆數，採用這個方式：
  		if(0 != $amt && $amt < count($ary))
  		{
	  		srand ((double) microtime() * 10000000);
			$ary_randIndex = array_rand($ary, $amt);
	
	
			//當指定的數量為1時，$ary_randIndex會是個數值，所以一律強制轉換成陣列，以免下面的foreach噴錯。
			$ary_randIndex = (array)$ary_randIndex;
			$ary_randItem = array();
	  		foreach($ary_randIndex as $eachIndex)
			{
				array_push($ary_randItem, $ary[$eachIndex]);
			}
			return $ary_randItem;
  		}
  		////因為回傳全部資料時洗牌洗得不夠亂，所以換這個方式：
		else
		{
			shuffle($ary);
			return $ary_randItem = $ary;
		}
  	}

	
	

	
	
}